/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.rational.parser.operation.function;

import java.math.BigDecimal;
import java.util.TreeSet;
import org.rational.parser.type.DefaultTypes;

/**
 *
 * @author Overmind
 */
public class DefaultFunctions
{
    private static TreeSet<Function> functions = null;
    
    ////////////////////////////////////////////////////////////////////////////
    //                      Математические                                    //
    ////////////////////////////////////////////////////////////////////////////
    
    public static final Function ABS = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("abs", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.abs(arg1.doubleValue()));
        }
    };
    
    public static final Function EXP = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("exp", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.exp(arg1.doubleValue()));
        }
    };
    
    public static final Function LOG = 
        new Function2Arguments<BigDecimal, BigDecimal, BigDecimal>
            ("log", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal value, BigDecimal index)
        {
            return BigDecimal.valueOf(Math.log(value.doubleValue()) / 
                    Math.log(index.doubleValue()));
        }
    };
    
    public static final Function LN = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("ln", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.log(arg1.doubleValue()));
        }
    };
    
    public static final Function LOG10 = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("log10", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.log10(arg1.doubleValue()));
        }
    };
    
    public static final Function MAX = 
        new Function2Arguments<BigDecimal, BigDecimal, BigDecimal>
            ("max", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1, BigDecimal arg2)
        {
            return BigDecimal.valueOf(Math.max(arg1.doubleValue(), arg2.doubleValue()));
        }
    };
    
    public static final Function MIN = 
        new Function2Arguments<BigDecimal, BigDecimal, BigDecimal>
            ("min", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1, BigDecimal arg2)
        {
            return BigDecimal.valueOf(Math.min(arg1.doubleValue(), arg2.doubleValue()));
        }
    };
    
    public static final Function RAND = 
        new Function0Arguments<BigDecimal>
            ("rand", DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate()
        {
            return BigDecimal.valueOf(Math.random());
        }
    };
    
    public static final Function SGN = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("sgn", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.signum(arg1.doubleValue()));
        }
    };
    
    public static final Function POW = 
        new Function2Arguments<BigDecimal, BigDecimal, BigDecimal>
            ("pow", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1, BigDecimal arg2)
        {
            return BigDecimal.valueOf(Math.pow(arg1.doubleValue(), arg2.doubleValue()));
        }
    };
    
    public static final Function SQRT = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("sqrt", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.pow(arg1.doubleValue(), 0.5));
        }
    };
    
    public static final Function NOD = 
        new Function2Arguments<Integer, Integer, Integer>
            ("nod", DefaultTypes.DECIMAL, DefaultTypes.DECIMAL, DefaultTypes.DECIMAL)
    {
        @Override
        public Integer calculate(Integer arg1, Integer arg2)
        {
            int a = arg1;
            int b = arg2;
            
            while((a !=0 ) & ( b!=0 ))
            {
                if(a > b)
                {
                    a %= b;
                }
                else
                {
                    b %= a;
                }
            }
            
            return a + b;
        }
    };
    
    public static final Function NOK = 
        new Function2Arguments<Integer, Integer, Integer>
            ("nok", DefaultTypes.DECIMAL, DefaultTypes.DECIMAL, DefaultTypes.DECIMAL)
    {
        @Override
        public Integer calculate(Integer arg1, Integer arg2)
        {
            int a = arg1;
            int b = arg2;
            
            while((a !=0 ) & ( b!=0 ))
            {
                if(a > b)
                {
                    a %= b;
                }
                else
                {
                    b %= a;
                }
            }
            
            return (arg1 * arg2) / (a + b);
        }
    };
    
    public static final Function INT = 
        new Function1Arguments<Integer, BigDecimal>
            ("int", DefaultTypes.DECIMAL, DefaultTypes.NUMERIC)
    {
        @Override
        public Integer calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(arg1.doubleValue()).intValue();
        }
    };
    
    public static final Function M_E = 
        new Function0Arguments<BigDecimal>
            ("e", DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate()
        {
            return BigDecimal.valueOf(Math.E);
        }
    };
    
    ////////////////////////////////////////////////////////////////////////////
    //                      Тригонометрические                                //
    ////////////////////////////////////////////////////////////////////////////
    
    public static final Function RADIAN = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("radian", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.toRadians(arg1.doubleValue()));
        }
    };
    
    public static final Function DEGREE = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("degree", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.toDegrees(arg1.doubleValue()));
        }
    };
    
    public static final Function COS = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("cos", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.cos(arg1.doubleValue()));
        }
    };
    
    public static final Function SIN = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("sin", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.sin(arg1.doubleValue()));
        }
    };
    
    public static final Function TAN = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("tan", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.tan(arg1.doubleValue()));
        }
    };
    
    public static final Function CTAN = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("ctan", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(1).divide(BigDecimal.valueOf(Math.tan(arg1.doubleValue())));
        }
    };
    
    public static final Function SINH = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("sinh", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.sinh(arg1.doubleValue()));
        }
    };
    
    public static final Function COSH = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("cosh", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.cosh(arg1.doubleValue()));
        }
    };
    
    public static final Function TANH = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("tanh", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.tanh(arg1.doubleValue()));
        }
    };
    
    public static final Function CTANH = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("ctanh", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(1).divide(BigDecimal.valueOf(Math.tanh(arg1.doubleValue())));
        }
    };
    
    public static final Function ASIN = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("asin", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.asin(arg1.doubleValue()));
        }
    };
    
    public static final Function ACOS = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("acos", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.acos(arg1.doubleValue()));
        }
    };
    
    public static final Function ATAN = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("atan", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(Math.atan(arg1.doubleValue()));
        }
    };
    
    public static final Function ACTAN = 
        new Function1Arguments<BigDecimal, BigDecimal>
            ("actan", DefaultTypes.NUMERIC, DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate(BigDecimal arg1)
        {
            return BigDecimal.valueOf(1).divide(BigDecimal.valueOf(Math.atan(arg1.doubleValue())));
        }
    };
    
    public static final Function M_PI = 
        new Function0Arguments<BigDecimal>
            ("pi", DefaultTypes.NUMERIC)
    {
        @Override
        public BigDecimal calculate()
        {
            return BigDecimal.valueOf(Math.PI);
        }
    };
    
    ////////////////////////////////////////////////////////////////////////////
    //                      Текстовые                                         //
    ////////////////////////////////////////////////////////////////////////////
    
    public static final Function LEN = 
        new Function1Arguments<Integer, String>
            ("len", DefaultTypes.DECIMAL, DefaultTypes.TEXTUAL)
    {
        @Override
        public Integer calculate(String arg1)
        {
            return arg1.length();
        }
    };
     
    
    private static void init()
    {
        functions = new TreeSet<>();
        
        functions.add(ABS);
        functions.add(ACOS);
        functions.add(ACTAN);
        functions.add(ASIN);
        functions.add(ATAN);
        functions.add(COS);
        functions.add(COSH);
        functions.add(CTAN);
        functions.add(CTANH);
        functions.add(DEGREE);
        functions.add(EXP);
        functions.add(INT);
        functions.add(LEN);
        functions.add(LN);
        functions.add(LOG);
        functions.add(LOG10);
        functions.add(MAX);
        functions.add(MIN);
        functions.add(M_E);
        functions.add(M_PI);
        functions.add(NOD);
        functions.add(NOK);
        functions.add(POW);
        functions.add(RADIAN);
        functions.add(RAND);
        functions.add(SGN);
        functions.add(SIN);
        functions.add(SINH);
        functions.add(SQRT);
        functions.add(TAN);
        functions.add(TANH);
    }
    
    public static TreeSet<Function> getFunctions()
    {
	if(functions == null)
	{
	    init();
	}
	
	return functions;
    }
}
